package com.logica.mdm.webfrontend.controllers;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;

import com.logica.mdm.webfrontend.dao.SearchPartyDao;
import com.logica.mdm.webfrontend.dao.beans.PartyResultBean;
import com.logica.mdm.webfrontend.embedding.EmbeddingAdapter;
import com.logica.mdm.webfrontend.embedding.EmbeddingResolver;
import com.logica.mdm.webfrontend.models.party.PartySearchParams;
import com.logica.mdm.webfrontend.models.party.PartySearchResult;
import com.logica.mdm.webfrontend.models.party.PartySearchUtils;
import com.logica.mdm.webfrontend.utils.JdbcPaginatedList;
import com.logica.mdm.webfrontend.utils.RequestWithParamsBean;
import com.logica.mdm.webfrontend.utils.URLUtils;

@Controller
@RequestMapping("/*/persons")
public class PersonsController {
	
	private static final Logger logger = LoggerFactory.getLogger(PersonsController.class);
	
	@Autowired
	protected EmbeddingResolver embeddingResolver;
	
	@Autowired
	protected SearchPartyDao searchPartyDao;

	/**
	 * Initialize the data binder.
	 * @param binder
	 */
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
        dateFormat.setLenient(false);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
    }

    /**
     * Main search and results page.
     * @param servletRequest
     * @param model
     * @param searchId
     * @param session
     * @return view name
     */
	@RequestMapping(value="/search", method=RequestMethod.GET)
	public String setupForm(HttpServletRequest servletRequest, Model model, @RequestParam(value="searchid", required=false) String searchId,  @RequestParam(value="baseUrl", required=false) String baseUrl, HttpSession session) {
		// Get the search params from session
		PartySearchParams partySearchParams = getPartySearchParams(session, searchId);
		if (partySearchParams == null)
			partySearchParams = new PartySearchParams();
		
		// Get the paginated list from session
		JdbcPaginatedList paginatedList = getPartyPaginatedList(session, searchId);
		if (paginatedList != null)
			paginatedList.parseRequestArgs(servletRequest);
		
		// Get the embedding adapter from the resolver
		EmbeddingAdapter embeddingAdapter = embeddingResolver.resolveEmbeddingAdapter(servletRequest);

		// update the baseUrl if present
		String finalBaseUrl = partySearchParams.getRootUrl();
		if (StringUtils.isEmpty(finalBaseUrl)) {
			if (baseUrl != null) {
				finalBaseUrl = baseUrl;
			} else if (StringUtils.isNotEmpty(embeddingAdapter.getBaseUrl())) {
				finalBaseUrl = embeddingAdapter.getBaseUrl();
			} else {
				finalBaseUrl = "";
			}
		}
		partySearchParams.setRootUrl(finalBaseUrl);
		
		// Add attributes to the model
		model.addAttribute("partySearchParams", partySearchParams);
		model.addAttribute("personsPaginatedList", paginatedList);
		model.addAttribute("addUrl", finalBaseUrl + embeddingAdapter.getPersonsAddUrl(partySearchParams));
		model.addAttribute("homeUrl", finalBaseUrl + embeddingAdapter.getPersonsHomeUrl());
		
		
		return "persons/search";
	}

	/**
	 * Search submission page. This will redirect to the main search and results page if successful.
	 * @param servletRequest
	 * @param partySearchParams
	 * @param result
	 * @param model
	 * @param session
	 * @return view name or redirection
	 */
	@RequestMapping(value = "/search", method = RequestMethod.POST)
	public String processSubmit(HttpServletRequest servletRequest, @Valid PartySearchParams partySearchParams, BindingResult result, Model model, HttpSession session) {
		// Get the embedding adapter from the request
		final EmbeddingAdapter embeddingAdapter = embeddingResolver.resolveEmbeddingAdapter(servletRequest);
		
		final String baseUrl = partySearchParams.getRootUrl() == null ? "" : partySearchParams.getRootUrl();
				
		// Check for errors
		if (result.hasErrors()) {
			model.addAttribute("addUrl", baseUrl + embeddingAdapter.getPersonsAddUrl(partySearchParams));
			model.addAttribute("homeUrl", baseUrl + embeddingAdapter.getPersonsHomeUrl());
			return "persons/search";
		}
		
		// Create the search request
		RequestWithParamsBean request = PartySearchUtils.buildSearchQueryRequest(partySearchParams);
		
		// Create a paginated list for the search results
		JdbcPaginatedList paginatedList = new JdbcPaginatedList(searchPartyDao, request) {
			@Override
			protected PartySearchResult recordToModel(PartyResultBean record) {
				PartySearchResult model = super.recordToModel(record);
				if (embeddingAdapter.getPersonsAddUrl(model) != null) {
					model.setAddUrl(baseUrl + embeddingAdapter.getPersonsAddUrl(model));
				}
				if (embeddingAdapter.getPersonsShowUrl(model) != null) {
					model.setShowUrl(baseUrl + embeddingAdapter.getPersonsShowUrl(model));
				}
				if (embeddingAdapter.getPersonsEditUrl(model) != null) {
					model.setEditUrl(baseUrl + embeddingAdapter.getPersonsEditUrl(model));
				}
				return model;
			}
		};
		
		// Initialize and retrieve the search token, then add it as a model attribute to pass it through the redirection
		String searchId = paginatedList.getSearchId();
		model.addAttribute("searchid", searchId);
		
		// Store the search params and paginated list in session
		setPartySearchParams(session, searchId, partySearchParams);
		setPartyPaginatedList(session, searchId, paginatedList);

		// Redirect to the GET page to avoid resubmit on refresh
		return "redirect:" + URLUtils.getRequestURIWithoutContext(servletRequest);
	}
	
	/**
	 * Error handling page.
	 * @param t
	 * @param servletRequest
	 * @return view name
	 */
	@ExceptionHandler
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	public String errorOccured(Throwable t, HttpServletRequest servletRequest) {
		logger.error(t.getMessage(), t);
		servletRequest.setAttribute("exception", t);
		return "persons/error";
	}

	/**
	 * Get the PartySearchParams from the session, for a given searchId.
	 * @param session
	 * @param searchId
	 * @return PartySearchParams
	 */
	@SuppressWarnings("unchecked")
	protected PartySearchParams getPartySearchParams(HttpSession session, String searchId) {
		PartySearchParams partySearchParams = null;
		if (searchId != null) {
			Map<String, PartySearchParams> searchParamsMap = (Map<String, PartySearchParams>) session.getAttribute("partySearchParams");
			if (searchParamsMap != null) {
				partySearchParams = searchParamsMap.get(searchId);
			}
		}
		return partySearchParams;
	}
	
	/**
	 * Set the PartySearchParams in session, for a given searchId.
	 * @param session
	 * @param searchId
	 * @param partySearchParams
	 */
	@SuppressWarnings("unchecked")
	protected void setPartySearchParams(HttpSession session, String searchId, PartySearchParams partySearchParams) {
		Map<String, PartySearchParams> searchParamsMap = (Map<String, PartySearchParams>) session.getAttribute("partySearchParams");
		if (searchParamsMap == null)
			searchParamsMap = new HashMap<String, PartySearchParams>();
		
		searchParamsMap.put(searchId, partySearchParams);
		session.setAttribute("partySearchParams", searchParamsMap);
	}
	
	/**
	 * Get the SiperianPaginatedList of PartySearchResult from the session, for a given searchId.
	 * @param session
	 * @param searchId
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected JdbcPaginatedList getPartyPaginatedList(HttpSession session, String searchId) {
		JdbcPaginatedList paginatedList = null;
		if (searchId != null) {
			Map<String, JdbcPaginatedList> listMap = (Map<String, JdbcPaginatedList>) session.getAttribute("partyPaginatedList");
			if (listMap != null) {
				paginatedList = listMap.get(searchId);
			}
		}
		return paginatedList;
	}
	
	/**
	 * Set the SiperianPaginatedList of PartySearchResult in session, for a given searchId.
	 * @param session
	 * @param searchId
	 * @param paginatedList
	 */
	@SuppressWarnings("unchecked")
	protected void setPartyPaginatedList(HttpSession session, String searchId, JdbcPaginatedList paginatedList) {
		Map<String, JdbcPaginatedList> listMap = (Map<String, JdbcPaginatedList>) session.getAttribute("partyPaginatedList");
		if (listMap == null)
			listMap = new HashMap<String, JdbcPaginatedList>();
		
		listMap.put(searchId, paginatedList);
		session.setAttribute("partyPaginatedList", listMap);
	}

}
